package m.common.model.util;

import com.alibaba.fastjson.JSONObject;
import m.common.model.Model;
import m.system.db.DBManager;
import m.system.db.DataRow;
import m.system.db.DataSet;
import m.system.exception.MException;

import java.sql.SQLException;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class ModelQuery {
    private final Class<? extends Model> clazz;
    private final Map<String,String> queryFieldMap;
    private final Map<String,String> collectFieldMap;
    private final List<QueryCondition> conditionList;
    private final List<QueryOrder> orderList;

    private <T extends Model> ModelQuery(Class<T> clazz) {
        this.clazz=clazz;
        this.queryFieldMap=new HashMap<>();
        this.collectFieldMap=new HashMap<>();
        this.conditionList=new ArrayList<>();
        this.orderList=new ArrayList<>();
    }
    /**
     * 初始化查询类
     */
    public static <T extends Model> ModelQuery init(Class<T> clazz) {
        return new ModelQuery(clazz);
    }

    /**
     * 添加一个或多个查询字段,自动生成表达式 a.b #{a.b}
     */
    public ModelQuery addField(String... names){
        for(String name : names){
            queryFieldMap.put(name,"#{"+name+"}");
        }
        return this;
    }

    /**
     * 添加一个查询字段和查询表达式  包含聚合函数请使用setCountExp方法
     */
    public ModelQuery addFieldExp(String name,String fieldExp){
        queryFieldMap.put(name,fieldExp);
        return this;
    }

    /**
     * 添加一个聚合字段和查询表达式  非聚合函数请使用setFieldExp方法
     */
    public ModelQuery addCollectExp(String name,String fieldExp){
        collectFieldMap.put(name,fieldExp);
        return this;
    }

    /**
     * 添加查询条件
     */
    public ModelQuery addCondition(QueryCondition... conds){
        for(QueryCondition cond : conds){
            if(null!=cond) conditionList.add(cond);
        }
        return this;
    }

    /**
     * 添加排序
     */
    public ModelQuery addOrder(QueryOrder... orders){
        for(QueryOrder order : orders){
            if(null!=order) orderList.add(order);
        }
        return this;
    }
    /**
     * 查询列表
     */
    public List<JSONObject> queryList() throws MException, SQLException {
        if(!collectFieldMap.isEmpty()) throw new MException("包含聚合字段请调用queryCollect方法");
        return query(null,false);
    }
    /**
     * 查询列表
     */
    public List<JSONObject> queryList(QueryPage page) throws MException, SQLException {
        if(!collectFieldMap.isEmpty()) throw new MException("包含聚合字段请调用queryCollect方法");
        return query(page,false);
    }
    /**
     * 查询汇总
     */
    public List<JSONObject> queryCollect() throws MException, SQLException {
        return query(null,true);
    }
    /**
     * 查询汇总
     */
    public List<JSONObject> queryCollect(QueryPage page) throws MException, SQLException {
        return query(page,true);
    }
    /**
     * 查询列表
     */
    public <T> List<T> queryList(Class<T> clazz) throws MException, SQLException {
        return query(clazz,null,false);
    }
    /**
     * 查询列表
     */
    public <T> List<T> queryList(Class<T> clazz,QueryPage page) throws MException, SQLException {
        return query(clazz,page,false);
    }
    /**
     * 查询汇总
     */
    public <T> List<T> queryCollect(Class<T> clazz) throws MException, SQLException {
        return query(clazz,null,true);
    }
    /**
     * 查询汇总
     */
    public <T> List<T> queryCollect(Class<T> clazz,QueryPage page) throws MException, SQLException {
        return query(clazz,page,true);
    }
    /**
     * 查询
     */
    private <T> List<T> query(Class<T> clazz, QueryPage page, boolean isGroup) throws SQLException, MException {
        List<JSONObject> ls=query(page,isGroup);
        List<T> newLs=new ArrayList<>();
        ls.forEach(item->newLs.add(item.toJavaObject(clazz)));
        return newLs;
    }
    /**
     * 查询
     */
    private List<JSONObject> query(QueryPage page, boolean isGroup) throws MException, SQLException {
        Set<String> fs=new HashSet<>();
        queryFieldMap.forEach((key,item)->fs.addAll(getFields(item)));
        collectFieldMap.forEach((key,item)->fs.addAll(getFields(item)));
        ModelQueryList query=ModelQueryList.instance(clazz,fs.toArray(new String[0]),page,QueryCondition.and(conditionList.toArray(new QueryCondition[0])),orderList.toArray(new QueryOrder[0]));
        QueryParameter qp=query.getQueryParameter();
        String sql=qp.getSql();
        String fieldStr=sql.substring(7,sql.indexOf(" FROM "));
        String fromStr=sql.substring(sql.indexOf(" FROM "),sql.indexOf(" ORDER BY "));
        String orderStr=sql.substring(sql.indexOf(" ORDER BY "));
        for(String f : fieldStr.split(",")){
            String[] fn=f.split(" ");
            for(String n : fs){
                if(fn[1].equals(query.getAlias4Field(n))){
                    queryFieldMap.replaceAll((k, v) -> queryFieldMap.get(k).replaceAll("#\\{" + n + "}", fn[0]));
                    collectFieldMap.replaceAll((k, v) -> collectFieldMap.get(k).replaceAll("#\\{" + n + "}", fn[0]));
                    break;
                }
            }
        }
        StringBuilder fieldSql=new StringBuilder();
        StringBuilder groupSql=new StringBuilder();
        queryFieldMap.forEach((key,item)->{
            fieldSql.append(item).append(" '").append(key).append("',");
            groupSql.append(item).append(",");
        });
        collectFieldMap.forEach((key,item)->{
            fieldSql.append(item).append(" '").append(key).append("',");
        });
        String newSql="SELECT "+fieldSql.substring(0,fieldSql.length()-1)+fromStr;
        if(groupSql.length()>0&&isGroup) newSql+=" GROUP BY "+groupSql.substring(0,groupSql.length()-1);
        newSql+=orderStr;
        DataSet ds=DBManager.executeQuery(newSql,qp.getValueList().toArray(new Object[0]));
        List<JSONObject> result=new ArrayList<>();
        for(DataRow row : ds.rows()){
            JSONObject obj=new JSONObject();
            queryFieldMap.keySet().forEach(key->fillJSONObject(obj,key,row.get(key)));
            collectFieldMap.keySet().forEach(key->fillJSONObject(obj,key,row.get(key)));
            result.add(obj);
        }
        return result;
    }
    private void fillJSONObject(JSONObject data,String name,Object value){
        int i=name.indexOf(".");
        if(i==-1){
            data.put(name,value);
        }else{
            String fn=name.substring(0, i);
            String newField=name.substring(i+1);
            JSONObject lm=data.getJSONObject(fn);
            if(null==lm){
                lm=new JSONObject();
                data.put(fn,lm);
            }
            fillJSONObject(lm,newField,value);
        }
    }
    private List<String> getFields(String exp){
        List<String> fs=new ArrayList<>();
        Pattern pattern=Pattern.compile("\\#\\{.+?\\}");
        Matcher matcher=pattern.matcher(exp);
        while(matcher.find()){
            String str=matcher.group();
            String ns=str.substring(2,str.length()-1);
            fs.add(ns);
        }
        return fs;
    }
    public static void main(String[] a) throws MException, SQLException {
//        List<AdminLogin> ls=ModelQuery.init(AdminLogin.class)
//            .addField("adminGroup.oid","adminGroup.name")
//            .addFieldExp("name","concat(#{userNo},'-',#{username})")
//            //.addCollectExp("count","count(#{oid})")
//            //.addCollectExp("num","sum(#{loginCount})")
//            //.addCondition(QueryCondition.eq("userNo","1"))
//            .queryList(AdminLogin.class,new QueryPage(0,10));
//        SystemUtil.println(ls);
    }
}
